<?php
class CorePhpValidation {

	const RULE_TYPE_CUSTOM = 'custom';
	const RULE_TYPE_IN_BUILT = 'in-built';
	const CUSTOM_FUNCTION_IDENTIFIER = 'custom_';
	const CUSTOM_FUNCTION_CALL_TYPE_INLINE = 'inline';
	const CUSTOM_FUNCTION_CALL_TYPE_STATIC = 'static';
	const CUSTOM_FUNCTION_CALL_TYPE_THIS = 'this';
	const CUSTOM_FUNCTION_CALL_TYPE_INSTANCE = 'instance';

	protected static $default_messages = array();
	private $rules = array();
	private $data = array();
	private $errors = array();
	private $validation_successful = false;
	private $rule_name;
	private $inner_rule_name;
	private $rule_parameters;
	private $rule_has_parameters;
	private $rule_type;


	private function __construct() {}

	public static function initializeValidation() {
		$class = get_called_class();
		return new $class();
	}

	public function setValidationRules($rules=array()) {
		if(is_array($rules) && sizeof($rules) > 0) {
			$this->rules = $rules;
		}
	}

	public function setValidationData($data_fields=array(), $data_files=array()) {
		if(is_array($data_fields) && is_array($data_files) && sizeof($data_fields) > 0) {
			$this->data = array_merge($data_fields, $data_files);
		}
	}

	public function valid() {
		$valid = $this->runValidation();
		return $valid;
	}

	public function getError($field_name) {
		$error = isset($this->errors[$field_name]) ? $this->errors[$field_name] : '';
		return $error;
	}

	public function getErrorCount() {
		return sizeof($this->errors);
	}

	public function getAllErrors() {
		return $this->errors;
	}

	protected function getDefaultMessage($rule_name) {
		self::$default_messages['required'] = 'required';
		self::$default_messages['number'] = 'number';
		self::$default_messages['pattern'] = 'pattern';
		self::$default_messages['checkboxRequired'] = 'checkboxRequired';
		self::$default_messages['fileIniSize'] = 'File is too large. Maximum allowed ' . ini_get('upload_max_filesize') . ' ~ Error Code: ' . UPLOAD_ERR_INI_SIZE;
		self::$default_messages['fileFormSize'] = 'File is too large ~ Error Code: ' . UPLOAD_ERR_FORM_SIZE;
		self::$default_messages['filePartial'] = 'File partially uploaded ~ Error Code: ' . UPLOAD_ERR_PARTIAL;
		self::$default_messages['fileNoTmpDir'] = 'Upload failed ~ Error Code: ' . UPLOAD_ERR_NO_TMP_DIR;
		self::$default_messages['fileCantWrite'] = "Upload failed. Can't write file on disk ~ Error Code: " . UPLOAD_ERR_CANT_WRITE;
		self::$default_messages['filePhpExtension'] = 'Upload failed ~ Error Code: ' . UPLOAD_ERR_EXTENSION;

		return isset(self::$default_messages[$rule_name]) ? self::$default_messages[$rule_name] : 'This field is in error';
	}

	private function runValidation() {
		foreach($this->rules as $field_name => $rule_configuration) {
			if(isset($rule_configuration['rules'])) {
				foreach($rule_configuration['rules'] as $rule_key => $rule_value) {
					$this->rule_parameters = array();
					$this->rule_parameters[] = isset($this->data[$field_name]) ? $this->data[$field_name] : '';

					if(is_array($rule_value)) {
						$this->rule_name = $rule_key;
						$this->rule_has_parameters = true;
					}else {
						$this->rule_name = $rule_value;
						$this->rule_has_parameters = false;
					}

					$rule_name_initials = substr($this->rule_name, 0, strlen(self::CUSTOM_FUNCTION_IDENTIFIER));

					$this->rule_type = self::RULE_TYPE_IN_BUILT;

					if($this->rule_has_parameters == true) {
						if($rule_name_initials == self::CUSTOM_FUNCTION_IDENTIFIER) {
							$this->rule_type = self::RULE_TYPE_CUSTOM;
							$custom_rule_call_type = isset($rule_value['call_type']) ? $rule_value['call_type'] : '';

							if(isset($rule_value['parameters'])) {
								foreach($rule_value['parameters'] as $rule_key_custom=>$rule_value_custom) {
									$this->rule_parameters[] = $rule_value_custom;
								}
							}
						}else {
							foreach($rule_value as $key => $value) {
								$this->rule_parameters[] = $value;
							}
						}
					}

					$valid = true;
					
					$callback = array($this, $this->rule_name);

					if($this->rule_type == self::RULE_TYPE_CUSTOM) {
						$custom_rule_name = str_replace(self::CUSTOM_FUNCTION_IDENTIFIER, "", $this->rule_name);

						if($custom_rule_call_type == self::CUSTOM_FUNCTION_CALL_TYPE_THIS) {
							$callback = array($this, $custom_rule_name);
						}elseif($custom_rule_call_type == self::CUSTOM_FUNCTION_CALL_TYPE_INLINE) {
							$callback = $custom_rule_name;
						}elseif($custom_rule_call_type == self::CUSTOM_FUNCTION_CALL_TYPE_STATIC) {
							if(isset($rule_value['call_options']['class_name'])) {
								$callback = array($rule_value['call_options']['class_name'], $custom_rule_name);
							}
						}elseif($custom_rule_call_type == self::CUSTOM_FUNCTION_CALL_TYPE_INSTANCE) {
							if(isset($rule_value['call_options']['instance'])) {
								$callback = array($rule_value['call_options']['instance'], $custom_rule_name);
							}
						}
					}

					if(is_callable($callback)) {
						$valid = call_user_func_array($callback, $this->rule_parameters);
					}

					$this->inner_rule_name = $this->rule_name;

					if($valid == false) {
						if(isset($rule_configuration['messages'][$this->inner_rule_name])) {
							$error = $rule_configuration['messages'][$this->inner_rule_name];
						}elseif(isset($rule_configuration['messages'][$this->rule_name][$this->inner_rule_name])) {
							$error = $rule_configuration['messages'][$this->rule_name][$this->inner_rule_name];
						}else {
							$error = $this->getDefaultMessage($this->inner_rule_name);
						}

						$this->errors[$field_name] = $error;
						
						break;
					}
				}
			}
		}

		if(sizeof($this->errors) == 0) {
			$this->validation_successful = true;
		}

		return $this->validation_successful;
	}

	private function required($value) {
		$valid = false;

		if(preg_match_all('/[\S*]/m', $value, $matches)) {
			$valid = true;
		}

		return $valid;
	}

	private function number($value) {
		$valid = empty($value) ? true : false;

		if(is_numeric($value)) {
			$valid = true;
		}

		return $valid;
	}

	private function alphaNumeric($value) {
		$valid = empty($value) ? true : false;

		if(preg_match_all('/^[a-z A-Z 0-9]+$/m', $value, $matches)) {
			$valid = true;
		}

		return $valid;
	}

	private function pattern($value, $regex) {
		$valid = empty($value) ? true : false;

		if(!empty($value) && preg_match_all($regex, $value, $matches)) {
			$valid = true;
		}

		return $valid;
	}

	private function exactLength($value, $length) {
		$valid = empty($value) ? true : false;

		if(!empty($value)) {
			if(strlen($value) == $length) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function minLength($value, $length) {
		$valid = empty($value) ? true : false;

		if(!empty($value)) {
			if(strlen($value) >= $length) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function maxLength($value, $length) {
		$valid = empty($value) ? true : false;

		if(!empty($value)) {
			if(strlen($value) <= $length) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function checkboxRequired($value) {
		$valid = false;

		if(is_array($value)) {
			if(sizeof($value) > 0) {
				$valid = true;
			}
		} else {
			if(!empty($value)) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function checkboxExactChecked($value, $length) {
		$valid = is_array($value) ? false : true;

		if(is_array($value)) {
			if(sizeof($value) == $length) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function checkboxMinChecked($value, $length) {
		$valid = is_array($value) ? false : true;

		if(is_array($value)) {
			if(sizeof($value) >= $length) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function checkboxMaxChecked($value, $length) {
		$valid = is_array($value) ? false : true;

		if(is_array($value)) {
			if(sizeof($value) <= $length) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function selectRequired($value, $empty_option_value='') {
		$valid = false;

		if(is_array($value)) {
			if(sizeof($value) > 0 && !in_array($empty_option_value, $value)) {
				$valid = true;
			}
		}else {
			if(!empty($value) || ($value != $empty_option_value)) {
				$valid = true;
			}
		}
		return $valid;
	}

	private function multiselectExactSelected($value, $length, $empty_option_value='') {
		$valid = is_array($value) ? false : true;

		if(is_array($value)) {
			if(sizeof($value) == $length && !in_array($empty_option_value, $value)) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function multiselectMinSelected($value, $length, $empty_option_value='') {
		$valid = is_array($value) ? false : true;

		if(is_array($value)) {
			if(sizeof($value) >= $length && !in_array($empty_option_value, $value)) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function multiselectMaxSelected($value, $length, $empty_option_value='') {
		$valid = is_array($value) ? false : true;

		if(is_array($value)) {
			if(sizeof($value) <= $length && !in_array($empty_option_value, $value)) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function matchFields($value, $field_name_to_match=NULL) {
		$valid = (empty($value) && isset($this->data[$field_name_to_match]) && empty($this->data[$field_name_to_match])) ? true : false;

		if(!empty($value) && !is_null($field_name_to_match) && isset($this->data[$field_name_to_match]) && !empty($this->data[$field_name_to_match])) {
			if($value === $this->data[$field_name_to_match]) {
				$valid = true;
			}
		}

		return $valid;
	}

	private function fileRequired($value) {
		$valid = true;

		if((isset($value['error']) && ($value['error'] == UPLOAD_ERR_NO_FILE)) || empty($value)) {
			$valid = false;
		}

		return $valid;
	}

	private function fileExtensions($value, $valid_extensions=array()) {
		$valid = true;

		if(isset($value['error']) && ($value['error'] != UPLOAD_ERR_NO_FILE)) {
			$arr = explode('.', $value['name']);
			$extension = $arr[sizeof($arr) - 1];

			if(!in_array($extension, $valid_extensions)){
				$valid = false;
			}
		}

		return $valid;
	}

	private function fileMimeTypes($value, $valid_mime_types=array()) {
		$valid = true;

		if(isset($value['error']) && ($value['error'] != UPLOAD_ERR_NO_FILE)) {
			if(!in_array($value['type'], $valid_mime_types)){
				$valid = false;
			}
		}

		return $valid;
	}

	private function fileMaxSize($value, $max_size) {
		$valid = true;

		if(isset($value['error']) && ($value['error'] != UPLOAD_ERR_NO_FILE)) {
			if($value['size'] > $max_size){
				$valid = false;
			}
		}

		return $valid;
	}

	private function fileUploadSuccess($value) {
		$valid = true;

		if(isset($value['error']) && ($value['error'] != UPLOAD_ERR_NO_FILE)) {
			switch($value['error']) {
				case UPLOAD_ERR_OK:
					$valid = true;
					break;

				case UPLOAD_ERR_INI_SIZE:
					$this->inner_rule_name = 'fileIniSize';
					$valid = false;
					break;

				case UPLOAD_ERR_FORM_SIZE:
					$this->inner_rule_name = 'fileFormSize';
					$valid = false;
					break;

				case UPLOAD_ERR_PARTIAL:
					$this->inner_rule_name = 'filePartial';
					$valid = false;
					break;

				case UPLOAD_ERR_NO_TMP_DIR:
					$this->inner_rule_name = 'fileNoTmpDir';
					$valid = false;
					break;

				case UPLOAD_ERR_CANT_WRITE:
					$this->inner_rule_name = 'fileCantWrite';
					$valid = false;
					break;

				case UPLOAD_ERR_EXTENSION:
					$this->inner_rule_name = 'filePhpExtension';
					$valid = false;
					break;
			}
		}

		return $valid;
	}
}
?>